For Statements
==============
.. raw:: html
For statements is my favorite feature of Python. It's hard to really
appreciate why it is so ground-breaking without knowing how other languages
solve this problem.
Syntax
------
.. code:: python
for in : SUITE
else: SUITE
```` is the same as for the assign statement. Parallel assignment is
OK, and I use it often. We'll have examples that include this later.
The simplest target is just a single variable name.
```` is an expression list. You can use star expressions or
whatever you want, as long as the expr list evaluates to an iterable of any
kind.
The ``for`` statement first creates an iterator of the ````.
Remember that it's OK to use an iterator, since the iterator of an iterator is
just the iterator.
Then, it calls ``next()`` on the iterator. If it raises ``StopIteration``
because it has exhausted all the items, then it will execute the ``else``
block, and continue with the next statement following the ``for`` statement.
If there is a value, then it assigns the value to the ```` as if
it were an assign statement. Then it runs the ``for`` block.
If the ``for`` block executes a ``continue``, or it finishes, then the
``next()`` value is grabbed from the iterator, and it continues.
If the ``for`` block executes a ``break`` statement, then the execution of the
``for`` block is interrupted, the ``else`` block bypassed, and execution
continues with the next statement after the ``for`` statement.
This can be rewritten as a while loop:
.. code:: python
i = iter()
while True:
try:
= next(i)
except StopIteration:
break
Understanding the Else Block
----------------------------
For a ``while`` loop, the ``else`` block is executed when the ``while``
condition evaluates to ``False``. It is the code that is run as long as
the ``break`` statement is never run.
For a ``for`` loop, the ``else`` block is executed when the ````
runs out of items. It is the code that is run as long as the ``break``
statement is never run.
Although it is quite rare to see an ``else`` block for a ``while`` loop, it is
actually pretty common to see an ``else`` block on a ``for`` loop. For
instance, you might iterate across a sequence, looking for a particular value.
If it is found, your code may ``break`` out of the loop, because it is
pointless to look any further. In this case, the ``else`` block is run if it
is *not* found.
Example: Simple Iteration
-------------------------
Typically, we use ``for`` loops to do something on each item in a sequence. In
this case, we're going to print out values in a sequence.
.. code:: python
items = (1, 4.0, 1+2j, "seventeen")
for i in items:
print(i)
Example: Two-deep Iteration
---------------------------
It's not uncommon to have tuples of tuples. As long as they are simple
nestings without arbitrary depth, we can use nested ``for`` loops to touch
each item. In this case, we want to add two matrices. Note that we iterate
across the indexes of each matrix.
.. code:: python
a = (
(0, 1, 4),
(3, 2, 8),
(1, 9, 7),
)
b = (
(1, 2, 1),
(0, 9, 2),
(3, 4, 6),
)
result = ()
for i in range(3):
row = ()
for j in range(3):
row = row + (a[i][j]+b[i][j],)
result = result + (row,)
It might be beneficial to take some time to understand the tuple addition in
the last few lines.
.. note::
Many programmers have a problem with short variable names such as ``i`` and
``j``. As long as these are used in the context of iteration of a sequence,
such as in a ``for`` statement, I have never had an issue with it.
``enumerate()``
---------------
If you want to iterate across a sequence and keep track of which index you are
at, then ``enumerate()`` is particularly powerful.
``enumerate()`` takes an iterable as an argument, and returns an iterator with
tuple pairs. The first is the index of the item, and the second is the item.
Let's play around with it a bit to understand how it works:
.. code:: python
>>> e = enumerate("Hello, World!")
>>> next(e)
(0, 'H')
>>> next(e)
(1, 'e')
>>> next(e)
(2, 'l')
>>> next(e)
(3, 'l')
>>> next(e)
(4, 'o')
>>> next(e)
(5, ',')
>>> next(e)
(6, ' ')
>>> next(e)
(7, 'W')
Used in a ``for`` loop, this is particularly powerful:
.. code:: python
for i, char in enumerate("Hello, World!"):
print("The character at", i, "is", char)
You can also set the starting index with the second parameter. I use this if I
have already cut up the sequence and want to get back to the original value.
.. code:: python
for i, char in enumerate("Hello, World!"[7:], 7):
print("The character at", i, "is", char)
Example: Finding an Item
------------------------
We already have the ``index()`` method to find where a particular element
resides in a tuple, a string, or a bytes object.
What if it isn't a simple compare we are after?
In this example, we're looking for the third odd value in a sequence.
.. code:: python
values = (1, 2, 6, 5, 9, 11, 12)
odds_seen = 0
for i, value in enumerate(values):
if value % 2 == 1:
odds_seen = odds_seen + 1
if odds_seen == 3: break
Note that the variables ``i`` and ``value`` persist after the ``for`` loop has
completed. (The only time variables don't persist is in an ``except`` block,
and that's only because keeping an exception around will prevent garbage
collection of the frames and stack from occurring.)
Let's turn the above into a pure function. Remember, pure functions don't rely
on global state and don't modify their arguments.
.. code:: python
def find_odds(values, num_odds=1):
odds_seen = 0
for i, value in enumerate(values):
if value % 2 == 1:
odds_seen = odds_seen + 1
if odds_seen == 3: break
return i
We could've just as easily returned from inside the ``for`` loop rather than
breaking.
Using Variables After the ``For`` Statement
-------------------------------------------
It may be tempting to always use the variables after a ``for`` loop, but this
can be dangerous. If the sequence that is being iterated across is empty, then
the variable will never get assigned.
This leads many programmers to avoid using the variables at all. I think this
is unfortunate, because there are cases where it can be very useful (see the
"Finding an Item" example above.) Just be sure to either check that the
sequence isn't empty, or catch and process the ``NameError`` exception if it
was never used.
Some people avoid using it out of ignorance of the way Python does things. For
instance, in some languages, the variables are not accessible outside of the
for loop body.
Limitations of the For Loop
---------------------------
The ``for`` loop is not a panacea. It is a convenience for particular cases
where iterating across each item in a sequence and doing something is useful
In particular, if you need to iterate across two loops in parallel, you can't
use the ``for`` loop unless you are doing something like the matrix example
above. IE, if you want to join two sequences together in sorted order,
grabbing the lowest value from one of the sequences, the ``for`` loop won't
help you.